﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BuildBunnySettingsViewModel.cs" company="Jim Liddell">
//   Copyright © Jim Liddell. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace TeamBuildBunnyConfiguration.ViewModels
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Input;

    using Microsoft.TeamFoundation.Proxy;

    using TeamBuildBunny;

    using TeamBuildBunnyConfiguration.Models;
    using TeamBuildBunnyConfiguration.Views;

    #endregion

    /// <summary>
    /// ViewModel class for <see cref="BuildBunnySettings"/>.
    /// </summary>
    public class BuildBunnySettingsViewModel : INotifyPropertyChanged
    {
        #region Constants and Fields

        /// <summary>
        /// The Model behind the ViewModel.
        /// </summary>
        private readonly BuildBunnySettingsModel dataModel;

        /// <summary>
        /// The currently selected culture.
        /// </summary>
        private CultureInfo culture;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BuildBunnySettingsViewModel"/> class.
        /// </summary>
        /// <param name="dataModel">
        /// The Model behind the ViewModel.
        /// </param>
        public BuildBunnySettingsViewModel(BuildBunnySettingsModel dataModel)
        {
            this.dataModel = dataModel;

            this.dataModel.PropertyChanged += delegate(object sender, PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "TfsUri")
                {
                    this.OnPropertyChanged(e.PropertyName);
                }
            };

            this.SetCulture();
            this.RegisterCommands();
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the culture for the <see cref="Nabaztag"/>.
        /// </summary>
        public CultureInfo Culture
        {
            get
            {
                return this.culture;
            }

            set
            {
                this.culture = value;

                // changing the culture changes the available voices
                this.OnPropertyChanged("Voices");
            }
        }

        /// <summary>
        /// Gets the cultures available to assign to the <see cref="Nabaztag"/>.
        /// </summary>
        public IEnumerable<CultureInfo> Cultures
        {
            get
            {
                return Nabaztag.Voices.GetCultures().OrderBy(c => c.DisplayName);
            }
        }

        /// <summary>
        /// Gets or sets the serial number for the <see cref="Nabaztag"/>.
        /// </summary>
        /// <exception cref="ApplicationException">
        /// </exception>
        public string SerialNumber
        {
            get
            {
                return this.dataModel.Nabaztag.Identity.SerialNumber;
            }

            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new ApplicationException("Serial Number is mandatory.");
                }

                this.dataModel.Nabaztag.Identity.SerialNumber = value;
            }
        }

        /// <summary>
        /// Gets the URI of the TFS server.
        /// </summary>
        public string TfsUri
        {
            get
            {
                return this.dataModel.TfsUri;
            }
        }

        /// <summary>
        /// Gets or sets the token for the <see cref="Nabaztag"/>.
        /// </summary>
        /// <exception cref="ApplicationException">
        /// </exception>
        public string Token
        {
            get
            {
                return this.dataModel.Nabaztag.Identity.Token;
            }

            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new ApplicationException("Token is mandatory.");
                }

                this.dataModel.Nabaztag.Identity.Token = value;
            }
        }

        /// <summary>
        /// Gets or sets the voice for the <see cref="Nabaztag"/>.
        /// </summary>
        public Nabaztag.Voice Voice
        {
            get
            {
                return this.dataModel.Nabaztag.Identity.Voice;
            }

            set
            {
                this.dataModel.Nabaztag.Identity.Voice = value;
            }
        }

        /// <summary>
        /// Gets the voices available to assign to the <see cref="Nabaztag"/>.
        /// </summary>
        public IEnumerable<Nabaztag.Voice> Voices
        {
            get
            {
                if (this.culture != null)
                {
                    // get the voices based on the currently selected culture
                    return Nabaztag.Voices.GetVoices(this.culture);
                }

                return null;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Shuts down the application.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The parameter is not used.
        /// </param>
        private static void CloseExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        /// <summary>
        /// Sets the <see cref="CanExecuteRoutedEventArgs.CanExecute"/> and <see cref="RoutedEventArgs.Handled"/> properties to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private static void CommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            e.Handled = true;
        }

        /// <summary>
        /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property that changed.
        /// </param>
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Registers <see cref="CommandBinding"/>s against the commands used in the <see cref="BuildBunnySettings"/>.
        /// </summary>
        private void RegisterCommands()
        {
            CommandBinding selectServerBinding = new CommandBinding(
                TfsCommands.SelectServer, this.SelectServerExecuted, CommandCanExecute);
            CommandBinding testNabaztagBinding = new CommandBinding(
                NabaztagCommands.Test, this.TestNabaztagExecuted, this.TestNabaztagCanExecute);
            CommandBinding subscribeToBuildCompletionEventBinding =
                new CommandBinding(
                    TfsCommands.SubscribeToBuildCompletionEvent, 
                    this.SubscribeToBuildCompletionEventExecuted, 
                    this.SubscribeToBuildCompletionEventCanExecute);
            CommandBinding unsubscribeFromBuildCompletionEventBinding =
                new CommandBinding(
                    TfsCommands.UnsubscribeFromBuildCompletionEvent, 
                    this.UnsubscribeFromBuildCompletionEventExecuted, 
                    this.UnsubscribeFromBuildCompletionEventCanExecute);
            CommandBinding subscribeToCheckInEventBinding = new CommandBinding(
                TfsCommands.SubscribeToCheckInEvent, 
                this.SubscribeToCheckInEventExecuted, 
                this.SubscribeToCheckInEventCanExecute);
            CommandBinding unsubscribeFromCheckInEventBinding =
                new CommandBinding(
                    TfsCommands.UnsubscribeFromCheckInEvent, 
                    this.UnsubscribeFromCheckInEventExecuted, 
                    this.UnsubscribeFromCheckInEventCanExecute);
            CommandBinding saveBinding = new CommandBinding(
                ApplicationCommands.Save, this.SaveExecuted, CommandCanExecute);
            CommandBinding closeBinding = new CommandBinding(
                ApplicationCommands.Close, CloseExecuted, CommandCanExecute);

            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), selectServerBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), testNabaztagBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), subscribeToBuildCompletionEventBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), unsubscribeFromBuildCompletionEventBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), subscribeToCheckInEventBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), unsubscribeFromCheckInEventBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), saveBinding);
            CommandManager.RegisterClassCommandBinding(typeof(BuildBunnySettings), closeBinding);
        }

        /// <summary>
        /// Saves the underlying Model for this ViewModel and shuts down the application.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The parameter is not used.
        /// </param>
        private void SaveExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.dataModel.Save();

            Application.Current.Shutdown();
        }

        /// <summary>
        /// Shows a <see cref="DomainProjectPicker"/> allowing the user to select a TFS server.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void SelectServerExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            DomainProjectPicker dpp = new DomainProjectPicker(DomainProjectPickerMode.None);

            if (dpp.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.dataModel.TfsUri = dpp.SelectedServer.Uri.ToString();
            }

            e.Handled = true;
        }

        /// <summary>
        /// Sets the culture to that of the <see cref="Nabaztag"/>.
        /// </summary>
        private void SetCulture()
        {
            this.culture = this.Voice.Culture;
        }

        /// <summary>
        /// Sets <see cref="CanExecuteRoutedEventArgs.CanExecute"/> appropriately and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private void SubscribeToBuildCompletionEventCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = 
                !string.IsNullOrEmpty(this.dataModel.TfsUri) &&
                this.dataModel.BuildCompletionEventSubscriptionId == -1;
            e.Handled = true;
        }

        /// <summary>
        /// Subscribes to the build completion event and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void SubscribeToBuildCompletionEventExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.dataModel.SubscribeToBuildCompletionEvent();
            e.Handled = true;
        }

        /// <summary>
        /// Sets <see cref="CanExecuteRoutedEventArgs.CanExecute"/> appropriately and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private void SubscribeToCheckInEventCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =
                !string.IsNullOrEmpty(this.dataModel.TfsUri) &&
                this.dataModel.CheckInEventSubscriptionId == -1;
            e.Handled = true;
        }

        /// <summary>
        /// Subscribes to the check in event and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void SubscribeToCheckInEventExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.dataModel.SubscribeToCheckInEvent();
            e.Handled = true;
        }

        /// <summary>
        /// Sets <see cref="CanExecuteRoutedEventArgs.CanExecute"/> appropriately and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private void TestNabaztagCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =
                !string.IsNullOrEmpty(this.dataModel.Nabaztag.Identity.SerialNumber) &&
                !string.IsNullOrEmpty(this.dataModel.Nabaztag.Identity.Token) &&
                this.dataModel.Nabaztag.Identity.Voice != null;
            e.Handled = true;
        }

        /// <summary>
        /// Sends a test message to the <see cref="Nabaztag"/>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void TestNabaztagExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            try
            {
                this.dataModel.TestNabaztag("The quick brown fox jumps over the lazy dog.");

                MessageBox.Show(
                    "A test message has been sent to the Nabaztag.",
                    "Team Build Bunny Settings",
                    MessageBoxButton.OK,
                    MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    string.Format("An error occured sending the test message: '{0}'.", ex.Message),
                    "Team Build Bunny Settings",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }

            e.Handled = true;
        }

        /// <summary>
        /// Sets <see cref="CanExecuteRoutedEventArgs.CanExecute"/> appropriately and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private void UnsubscribeFromBuildCompletionEventCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =
                !string.IsNullOrEmpty(this.dataModel.TfsUri) &&
                this.dataModel.BuildCompletionEventSubscriptionId != -1;
            e.Handled = true;
        }

        /// <summary>
        /// The unsubscribe from build completion event executed.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void UnsubscribeFromBuildCompletionEventExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.dataModel.UnsubscribeFromBuildCompletionEvent();
            e.Handled = true;
        }

        /// <summary>
        /// Sets <see cref="CanExecuteRoutedEventArgs.CanExecute"/> appropriately and sets <see cref="RoutedEventArgs.Handled"/> to <c>true</c>.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="CanExecuteRoutedEventArgs"/>.
        /// </param>
        private void UnsubscribeFromCheckInEventCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =
                !string.IsNullOrEmpty(this.dataModel.TfsUri) &&
                this.dataModel.CheckInEventSubscriptionId != -1;
            e.Handled = true;
        }

        /// <summary>
        /// The unsubscribe from check in event executed.
        /// </summary>
        /// <param name="sender">
        /// The parameter is not used.
        /// </param>
        /// <param name="e">
        /// The <see cref="ExecutedRoutedEventArgs"/>.
        /// </param>
        private void UnsubscribeFromCheckInEventExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            this.dataModel.UnsubscribeFromCheckInEvent();
            e.Handled = true;
        }

        #endregion
    }
}